package idao.engine;

import idao.ConnectionManager;

import java.lang.reflect.Method;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

/**
 * Base class for the dynamic dao proxy class created with Javassist.
 * 
 * @author huhao
 */
class JavassistDaoProxy extends DaoProxy implements DaoProxyBuilder
{

    public JavassistDaoProxy() throws ClassNotFoundException
    {
        Class.forName("javassist.util.proxy.ProxyFactory");
    }

    private static final Object[] EMPTY_ARGS = {};
    private static final Class<?>[] EMPTY_TYPES = {};

    public <T> T newDaoProxy(final Class<T> dao, ConnectionManager cm)
    {
        ProxyFactory pf = new ProxyFactory()
        {
            @Override
            protected ClassLoader getClassLoader()
            {
                // using the same class loader with dao interface
                return dao.getClassLoader();
            }
        };
        pf.setSuperclass(DaoProxy.class);
        pf.setInterfaces(new Class[] { dao });
        pf.setFilter(DaoMethodFilter.INSTANCE);
        // ready to create new proxy instance
        try {
            return dao.cast(pf.create(EMPTY_TYPES, EMPTY_ARGS, new DaoMethodHandler(cm)));
        } catch (Exception e) {
            throw transfromException(e, dao);
        }
    }

    private class DaoMethodHandler implements MethodHandler
    {
        private ConnectionManager cm;

        DaoMethodHandler(ConnectionManager cm)
        {
            this.cm = cm;
        }

        public Object invoke(Object proxy, Method m, Method p, Object[] args) throws Throwable
        {
            return invokeMethod(m, args, cm);
        }
    }

    /**
     * Do not hander method inherited from class {@code Object}.
     */
    private static class DaoMethodFilter implements MethodFilter
    {

        static final MethodFilter INSTANCE = new DaoMethodFilter();

        /**
         * Returns {@code true} if the method is not delcared in {@code Object}.
         */
        public boolean isHandled(Method m)
        {
            return (Object.class != m.getDeclaringClass());
        }
    }
}
